package com.seed.core.query;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.BaseMapper;
import com.mybatisflex.core.mybatis.Mappers;
import com.mybatisflex.core.row.Db;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.core.row.RowKey;
import com.seed.core.enumeration.PropertyType;
import com.seed.core.listener.OnSaveListener;
import com.seed.core.pojo.Const;
import com.seed.core.pojo.seed.*;
import com.seed.core.query.pojo.Declared;
import com.seed.util.RowKit;
import com.seed.util.SeedKit;

import java.util.*;

@SuppressWarnings("unchecked")
public class SeedSave {

//    public static <T> void save(Seed seed, Map<String, Declared> declareds, T data) {
//        T validData = getValidData(seed, declareds, data);
//        List<? extends OnSaveListener> listeners = seed.getOnSave();
//        if(listeners != null) {
//            for(OnSaveListener listener: listeners) listener.onSave(validData);
//        }
//
//        if (data instanceof Row row) {
//            saveForRow(seed, declareds, row);
//        } else {
//            saveForObject(declareds, data);
//        }
//    }
//
//    public static <T> void saveBatch(Seed seed, Map<String, Declared> declareds, List<T> data) {
//        if(data.isEmpty()) return;
//
//        if(isPlain(declareds)) {
//            List<Object> list = new ArrayList<>();
//            for(T t: data) {
//                T validData = getValidData(seed, declareds, t);
//                List<? extends OnSaveListener> listeners = seed.getOnSave();
//                if(listeners != null) {
//                    for(OnSaveListener listener: listeners) listener.onSave(validData);
//                }
//                list.add(t);
//            }
//
//            T t = data.get(0);
//            if(t instanceof Row) {
//                List<Row> rows = list.stream().map(e -> (Row) e).toList();
//                Db.insertBatch(seed.getTableName(), rows);
//            } else {
//                BaseMapper<T> baseMapper = Mappers.ofEntityClass((Class<T>) t.getClass());
//                List<T> entities = list.stream().map(e -> (T) e).toList();
//                baseMapper.insertBatch(entities);
//            }
//        } else {
//            for (T t: data) save(seed, declareds, t);
//        }
//    }
//
//    public static void saveForRow(Seed seed, Map<String, Declared> declareds, Row self) {
//        Row row = Row.ofKey(RowKey.FLEX_ID);
//        for(Map.Entry<String, Declared> entry: declareds.entrySet()) {
//            String crossOrColumn = entry.getKey();
//            Declared value = entry.getValue();
//            PropertyType propertyType = value.getType();
//            switch (propertyType) {
//                case none, asterisk -> {
//                    Map<String, Column> columns = seed.getColumns();
//                    for (Column column: columns.values()) {
//                        String columName = column.getName();
//                        if(Const.ID.equals(columName)) continue;
//
//                        row.set(columName, self.get(StrUtil.toCamelCase(columName)));
//                    }
//                }
//                case column -> {
//                    Column column = (Column) value.getProperty();
//                    String columName = column.getName();
//                    if(Const.ID.equals(columName)) continue;
//
//                    row.set(columName, self.get(StrUtil.toCamelCase(columName)));
//                }
//                case belongsTo -> {
//                    BelongsTos belongsTo = (BelongsTos) value.getProperty();
//                    String fieldName = StrUtil.toCamelCase(crossOrColumn);
//                    Row target = (Row) self.get(fieldName);
////                    if(belongsTo.getExtraType() != ExtraType.code) {
////                        String inferSelfColumn = belongsTo.inferSelfColumn();
////                        String targetField = StrUtil.toCamelCase(belongsTo.inferTargetColumn());
////                        row.set(inferSelfColumn, target == null ? null : target.get(targetField));
////                    }
//                    String inferSelfColumn = belongsTo.inferSelfColumn();
//                    String targetField = StrUtil.toCamelCase(belongsTo.inferTargetColumn());
//                    row.set(inferSelfColumn, target == null ? null : target.get(targetField));
//                }
//            }
//        }
//
//        Db.insert(seed.getTableName(), row);
//        for(Map.Entry<String, Declared> entry: declareds.entrySet()) {
//            String crossOrColumn = entry.getKey();
//            String fieldName = StrUtil.toCamelCase(crossOrColumn);
//            Declared value = entry.getValue();
//            PropertyType propertyType = value.getType();
//            switch (propertyType) {
//                case hasOne -> {
//                    HasOnes hasOne = (HasOnes) value.getProperty();
//                    Seed targetSeed = hasOne.targetSeed();
//                    Row target = (Row) self.get(fieldName);
//                    if(target == null) continue;
//
////                    if (hasOne.getExtraType() != ExtraType.code) {
////                        String inferSelfColumn = hasOne.inferSelfColumn();
////                        String targetField = StrUtil.toCamelCase(hasOne.inferTargetColumn());
////                        target.set(targetField, row.get(inferSelfColumn));
////                    }
//                    String inferSelfColumn = hasOne.inferSelfColumn();
//                    String targetField = StrUtil.toCamelCase(hasOne.inferTargetColumn());
//                    target.set(targetField, row.get(inferSelfColumn));
//                    saveForRow(targetSeed, value, target);
//                }
//                case hasMany -> {
//                    Object targetObj = self.get(fieldName);
//                    if (targetObj == null) return;
//
//                    HasManys hasMany = (HasManys) value.getProperty();
//                    Seed targetSeed = hasMany.targetSeed();
//
//                    Collection<Row> target;
//                    if(targetObj.getClass().isArray()) {
//                        target = List.of((Row[]) targetObj);
//                    } else if(targetObj instanceof Collection<?>) {
//                        target = (Collection<Row>) targetObj;
//                    } else {
//                        throw new RuntimeException(String.format("不支持的类型【%s】", targetObj.getClass()));
//                    }
//
//                    List<Row> adding = new ArrayList<>();
//                    for(Map<String, Object> map: target) {
//                        Row t = RowKit.toRow(map);
////                        if(hasMany.getExtraType() != ExtraType.code) {
////                            String inferSelfColumn = hasMany.inferSelfColumn();
////                            String targetField = StrUtil.toCamelCase(hasMany.inferTargetColumn());
////                            t.set(targetField, row.get(inferSelfColumn));
////                        }
//                        String inferSelfColumn = hasMany.inferSelfColumn();
//                        String targetField = StrUtil.toCamelCase(hasMany.inferTargetColumn());
//                        t.set(targetField, row.get(inferSelfColumn));
//                        adding.add(t);
//                    }
//                    saveBatch(targetSeed, value, adding);
//                }
//                case habtm -> {
//                    Object targetObj = self.get(fieldName);
//                    if(targetObj == null) continue;
//
//                    Habtms habtm = (Habtms) value.getProperty();
//
//                    Collection<Row> target;
//                    if(targetObj.getClass().isArray()) {
//                        target = List.of((Row[]) targetObj);
//                    } else if(targetObj instanceof Collection<?>) {
//                        target = (Collection<Row>) targetObj;
//                    } else {
//                        throw new RuntimeException(String.format("不支持的类型【%s】", targetObj.getClass()));
//                    }
//
////                    if(habtm.getExtraType() != ExtraType.code) {
////                        String inferSelfColumn = habtm.inferSelfColumn();
////                        String targetField = StrUtil.toCamelCase(habtm.inferTargetColumn());
////                        String inferThroughSelfColumn = habtm.inferThroughSelfColumn();
////                        String inferThroughTargetColumn = habtm.inferThroughTargetColumn();
////
////                        List<Row> adding = new ArrayList<>();
////                        for(Row t: target) {
////                            Row ret = Row.ofKey(RowKey.FLEX_ID);
////                            ret.set(inferThroughSelfColumn, row.get(inferSelfColumn));
////                            ret.set(inferThroughTargetColumn, t.get(targetField));
////                            adding.add(ret);
////                        }
////
////                        String through = habtm.throughTableName();
////                        if(!adding.isEmpty()) Db.insertBatch(through, adding);
////                    }
//
//                    String inferSelfColumn = habtm.inferSelfColumn();
//                    String targetField = StrUtil.toCamelCase(habtm.inferTargetColumn());
//                    String inferThroughSelfColumn = habtm.inferThroughSelfColumn();
//                    String inferThroughTargetColumn = habtm.inferThroughTargetColumn();
//
//                    List<Row> adding = new ArrayList<>();
//                    for(Row t: target) {
//                        Row ret = Row.ofKey(RowKey.FLEX_ID);
//                        ret.set(inferThroughSelfColumn, row.get(inferSelfColumn));
//                        ret.set(inferThroughTargetColumn, t.get(targetField));
//                        adding.add(ret);
//                    }
//
//                    String through = habtm.throughTableName();
//                    if(!adding.isEmpty()) Db.insertBatch(through, adding);
//                }
//            }
//        }
//    }
//
//    public static <T> void saveForObject(Map<String, Declared> declareds, T self) {
//        for(Map.Entry<String, Declared> entry: declareds.entrySet()) {
//            String crossOrColumn = entry.getKey();
//            String fieldName = StrUtil.toCamelCase(crossOrColumn);
//            Declared value = entry.getValue();
//            PropertyType propertyType = value.getType();
//            switch (propertyType) {
//                case belongsTo -> {
//                    BelongsTos belongsTo = (BelongsTos) value.getProperty();
//                    Object target = RowKit.getFieldValue(self, fieldName);
////                    if(belongsTo.getExtraType() != ExtraType.code) {
////                        String selfField = StrUtil.toCamelCase(belongsTo.inferSelfColumn());
////                        String targetField = StrUtil.toCamelCase(belongsTo.inferTargetColumn());
////                        RowKit.setFieldValue(self, selfField, target == null ? null : RowKit.getFieldValue(target, targetField));
////                    }
//                    String selfField = StrUtil.toCamelCase(belongsTo.inferSelfColumn());
//                    String targetField = StrUtil.toCamelCase(belongsTo.inferTargetColumn());
//                    RowKit.setFieldValue(self, selfField, target == null ? null : RowKit.getFieldValue(target, targetField));
//                }
//                case hasOne -> {
//                    HasOnes hasOne = (HasOnes) value.getProperty();
//                    Object target = RowKit.getFieldValue(self, fieldName);
//                    if(target == null) continue;
//
////                    if (hasOne.getExtraType() != ExtraType.code) {
////                        String selfField = StrUtil.toCamelCase(hasOne.inferSelfColumn());
////                        String targetField = StrUtil.toCamelCase(hasOne.inferTargetColumn());
////                        RowKit.setFieldValue(target, targetField, RowKit.getFieldValue(self, selfField));
////                    }
//                    String selfField = StrUtil.toCamelCase(hasOne.inferSelfColumn());
//                    String targetField = StrUtil.toCamelCase(hasOne.inferTargetColumn());
//                    RowKit.setFieldValue(target, targetField, RowKit.getFieldValue(self, selfField));
//                    saveForObject(value, target);
//                }
//                case hasMany -> {
//                    Object targetObj = RowKit.getFieldValue(self, fieldName);
//                    if (targetObj == null) continue;
//
//                    HasManys hasMany = (HasManys) value.getProperty();
//                    Seed targetSeed = hasMany.targetSeed();
//                    String selfField = StrUtil.toCamelCase(hasMany.inferSelfColumn());
//                    String targetField = StrUtil.toCamelCase(hasMany.inferTargetColumn());
//
//                    Collection<Row> target;
//                    if(targetObj.getClass().isArray()) {
//                        target = List.of((Row[]) targetObj);
//                    } else if(targetObj instanceof Collection<?>) {
//                        target = (Collection<Row>) targetObj;
//                    } else {
//                        throw new RuntimeException(String.format("不支持的类型【%s】", targetObj.getClass()));
//                    }
//
//                    List<Row> adding = target.stream()
//                            .peek(e -> e.set(targetField, RowKit.getFieldValue(self, selfField))).toList();
//                    saveBatch(targetSeed, value, adding);
//                }
//                case habtm -> {
//                    Object targetObj = RowKit.getFieldValue(self, fieldName);
//                    if(targetObj == null) return;
//
//                    Habtms habtm = (Habtms) value.getProperty();
//                    String through = habtm.throughTableName();
//                    String selfField = StrUtil.toCamelCase(habtm.inferSelfColumn());
//                    String targetField = StrUtil.toCamelCase(habtm.inferTargetColumn());
//                    String throughSelfField = StrUtil.toCamelCase(habtm.inferThroughSelfColumn());
//                    String throughTargetField = StrUtil.toCamelCase(habtm.inferThroughTargetColumn());
//
//                    Collection<Row> target;
//                    if(targetObj.getClass().isArray()) {
//                        target = List.of((Row[]) targetObj);
//                    } else if(targetObj instanceof Collection<?>) {
//                        target = (Collection<Row>) targetObj;
//                    } else {
//                        throw new RuntimeException(String.format("不支持的类型【%s】", targetObj.getClass()));
//                    }
//
////                    if(habtm.getExtraType() != ExtraType.code) {
////                        List<Row> adding = new ArrayList<>();
////                        for(Row row: target) {
////                            Row ret = Row.ofKey(RowKey.FLEX_ID);
////                            ret.set(throughSelfField, RowKit.getFieldValue(self, selfField));
////                            ret.set(throughTargetField, row.get(targetField));
////                            adding.add(ret);
////                        }
////                        if(!adding.isEmpty()) Db.insertBatch(through, adding);
////                    }
//                    List<Row> adding = new ArrayList<>();
//                    for(Row row: target) {
//                        Row ret = Row.ofKey(RowKey.FLEX_ID);
//                        ret.set(throughSelfField, RowKit.getFieldValue(self, selfField));
//                        ret.set(throughTargetField, row.get(targetField));
//                        adding.add(ret);
//                    }
//                    if(!adding.isEmpty()) Db.insertBatch(through, adding);
//                }
//            }
//        }
//
//        BaseMapper<T> baseMapper = Mappers.ofEntityClass((Class<T>) self.getClass());
//        baseMapper.insert(self);
//    }
//
//    public static <T> T getValidData(Seed seed, Map<String, Declared> declareds, T self) {
//        Object ret = self instanceof Row ? Row.ofKey(RowKey.FLEX_ID) : ReflectUtil.newInstance(self.getClass());
//        for(Map.Entry<String, Declared> entry: declareds.entrySet()) {
//            String crossOrColumn = entry.getKey();
//            String fieldName = StrUtil.toCamelCase(crossOrColumn);
//            Declared value = entry.getValue();
//            PropertyType propertyType = value.getType();
//            switch (propertyType) {
//                case none, asterisk -> {
//                    Map<String, Column> columns = SeedKit.getColumns(seed);
//                    for (Column column: columns.values()) {
//                        SeedValidator.validate(seed, column, self);
//                        fieldName = StrUtil.toCamelCase(column.getName());
//                        Object fieldValue = RowKit.getSelfValue(self, fieldName);
//                        RowKit.setSelfValue(ret, fieldName, fieldValue);
//                    }
//                }
//                case column -> {
//                    Column column = (Column) value.getProperty();
//                    SeedValidator.validate(seed, column, self);
//                    Object fieldValue = RowKit.getSelfValue(self, fieldName);
//                    RowKit.setSelfValue(ret, fieldName, fieldValue);
//                }
//                case belongsTo -> {
//                    Relation relation = (Relation) value.getProperty();
//                    Object target = RowKit.getSelfValue(self, fieldName);
//                    if(target != null) {
////                        if(relation.getExtraType() != ExtraType.code) {
////                            String selfField = StrUtil.toCamelCase(relation.inferSelfColumn());
////                            String targetField = StrUtil.toCamelCase(relation.inferTargetColumn());
////                            RowKit.setSelfValue(ret, selfField, RowKit.getSelfValue(target, targetField));
////                        }
//                        String selfField = StrUtil.toCamelCase(relation.inferSelfColumn());
//                        String targetField = StrUtil.toCamelCase(relation.inferTargetColumn());
//                        RowKit.setSelfValue(ret, selfField, RowKit.getSelfValue(target, targetField));
////                        if(relation.getCondition() == null) {
////                            String selfField = StrUtil.toCamelCase(relation.inferSelfColumn());
////                            String targetField = StrUtil.toCamelCase(relation.inferTargetColumn());
////                            RowKit.setSelfValue(ret, selfField, RowKit.getSelfValue(target, targetField));
////                        } else {
////                            String selfColumn = relation.getSelfColumn();
////                            String targetColumn = relation.getTargetColumn();
////                            if(StrUtil.isNotBlank(selfColumn) || StrUtil.isNotBlank(targetColumn)) {
////                                String selfField = StrUtil.toCamelCase(relation.inferSelfColumn());
////                                String targetField = StrUtil.toCamelCase(relation.inferTargetColumn());
////                                RowKit.setSelfValue(ret, selfField, RowKit.getSelfValue(target, targetField));
////                            }
////                        }
//                    }
//                }
//                case hasOne -> {
//                    Relation relation = (Relation) value.getProperty();
//                    Seed targetSeed = relation.targetSeed();
//                    Object oldFieldValue = RowKit.getSelfValue(self, fieldName);
//                    if(oldFieldValue == null) {
//                        RowKit.setSelfValue(ret, fieldName, null);
//                    } else {
//                        Object fieldValue = getValidData(targetSeed, value, oldFieldValue);
//                        RowKit.setSelfValue(ret, fieldName, fieldValue);
//                    }
//                }
//                case hasMany -> {
//                    Relation relation = (Relation) value.getProperty();
//                    Seed targetSeed = relation.targetSeed();
//                    Object oldFieldValue = RowKit.getSelfValue(self, fieldName);
//                    Object fieldValue;
//                    if(oldFieldValue.getClass().isArray()) {
//                        fieldValue = getValidArray(targetSeed, value, (Object[]) oldFieldValue);
//                    } else if(oldFieldValue instanceof Set<?>) {
//                        fieldValue = getValidSet(targetSeed, value, (Set<Object>) oldFieldValue);
//                    } else if(oldFieldValue instanceof List<?>) {
//                        fieldValue = getValidList(targetSeed, value, (List<Object>) oldFieldValue);
//                    } else {
//                        throw new RuntimeException(String.format("不支持的类型【%s】", oldFieldValue.getClass()));
//                    }
//                    RowKit.setSelfValue(ret, fieldName, fieldValue);
//                }
//                default -> RowKit.setSelfValue(ret, fieldName, RowKit.getSelfValue(self, fieldName));
//            }
//        }
//
//        return (T) ret;
//    }
//
//    private static <T> T[] getValidArray(Seed seed, Map<String, Declared> declareds, T[] data) {
//        List<T> ret = new ArrayList<>();
//        for(T obj: data) ret.add(getValidData(seed, declareds, obj));
//
//        return (T[]) ret.toArray();
//    }
//
//    private static <T> Set<T> getValidSet(Seed seed, Map<String, Declared> declareds, Set<T> data) {
//        Set<T> ret = new HashSet<>();
//        for(T obj: data) ret.add(getValidData(seed, declareds, obj));
//
//        return ret;
//    }
//
//    private static <T> List<T> getValidList(Seed seed, Map<String, Declared> declareds, List<T> data) {
//        List<T> ret = new ArrayList<>();
//        for(T obj: data) ret.add(getValidData(seed, declareds, obj));
//
//        return ret;
//    }
//
////    private static <T> void validate(Seed seed, Column column, T data) {
////        String tableName = seed.getTableName();
////        String columnName = column.getName();
////        String label = StrUtil.isBlank(column.getLabel()) ? columnName : column.getLabel();
////        ColumnType columnType = column.getType();
////        String fieldName = StrUtil.toCamelCase(columnName);
////        Object value = RowKit.getSelfValue(data, fieldName);
////
////        Boolean required = column.getRequired();
////        Boolean unique = column.getUnique();
////        String min = (String) column.getMin();
////        String max = (String) column.getMax();
////        Integer length = column.getLength();
////
////        if(required != null && required && StrUtil.isBlank((String) value)) throw new RuntimeException(String.format("【%s】不能为空", label));
////
////        if(unique != null && unique) {
////            int count = (int) DbChain.table(tableName)
////                .select(QueryMethods.count())
////                .where(new QueryColumn(columnName).eq(value))
////                .obj();
////            if(count > 0) throw new RuntimeException(String.format("%s【%s】已存在", label, value));
////        }
////
////        switch (columnType) {
////            case CHAR -> {
////                if(value == null) return;
////                String val = value instanceof Long v ? Long.toString(v) : (String) value;
////                int len = val.length();
////                if(len != length) throw new RuntimeException(String.format("%s长度必需等于【%s】", label, length));
////            }
////            case TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT -> {
////                if(StrUtil.isNotBlank((CharSequence) value)) {
////                    long v = Long.parseLong((String) value);
////                    if(min != null) {
////                        long mi = Long.parseLong(min);
////                        if(v < mi) throw new RuntimeException(String.format("%s不能小于【%s】", label, mi));
////                    }
////
////                    if(max != null) {
////                        long ma = Long.parseLong(max);
////                        if(v > ma) throw new RuntimeException(String.format("%s不能大于【%s】", label, ma));
////                    }
////                }
////            }
////            case DOUBLE, FLOAT -> {
////                double v = Double.parseDouble((String) value);
////                if(min != null) {
////                    double mi = Double.parseDouble(min);
////                    if(v < mi) throw new RuntimeException(String.format("%s不能小于【%s】", label, mi));
////                }
////
////                if(max != null) {
////                    double ma = Double.parseDouble(max);
////                    if(v > ma) throw new RuntimeException(String.format("%s不能大于【%s】", label, ma));
////                }
////            }
////            case DECIMAL -> {
////                BigDecimal v = new BigDecimal((String) value);
////                if(min != null) {
////                    BigDecimal mi = new BigDecimal(min);
////                    if(v.compareTo(mi) < 0) throw new RuntimeException(String.format("%s不能小于【%s】", label, mi));
////                }
////
////                if(max != null) {
////                    BigDecimal ma = new BigDecimal(max);
////                    if(v.compareTo(ma) < 0) throw new RuntimeException(String.format("%s不能大于【%s】", label, ma));
////                }
////            }
////            default -> {
////                if(value instanceof YesNo yesNo) return;
////
////                int len = StrUtil.isBlank((String) value) ? 0 : ((String) value).length();
////                if(min != null) {
////                    int mi = Integer.parseInt(min);
////                    if(len < mi) throw new RuntimeException(String.format("%s长度不能小于【%s】", label, mi));
////                }
////
////                if(max != null) {
////                    int ma = Integer.parseInt(max);
////                    if(len > ma) throw new RuntimeException(String.format("%s长度不能大于【%s】", label, ma));
////                }
////            }
////        }
////    }
//
//    /**
//     * 是否只存在一级数据
//     * @param declareds 数据申明
//     * @return bool
//     */
//    private static boolean isPlain(Map<String, Declared> declareds) {
//        for (Declared declared: declareds.values()) {
//            if (!declared.getType().equals(PropertyType.column)) return false;
//        }
//
//        return true;
//    }
}
